之前翻译过一片关于Event Loop的文章,有了一个简单的了解,那么浏览器中的Event Loop和Node中有什么区别呢,下面用几个示例来探究下。

首先收悉一下之前的概念:
task任务:setTimeout setInterval setImmediate I/O UI交互事件
microtask微任务:Promsie process.nextTick MutaionObserver
再次放上一张经典的图(Node)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘

setTimeout setInterval

这两个在浏览器和Node的功效是一样的,意思都是过一定的时间段后执行或者循环某些函数。

setImmediate

这是Node中独有的操作,功效和setTimeout差不多,差别在于

process.nextTick

简单来讲

测试0:

1
2
3
4
5
6
7
8
9
10
11
12
13
setTimeout(() => {
console.log('1');
process.nextTick(()=>{
console.log('2');
})
});

setTimeout(() => {
console.log('3');
process.nextTick(()=>{
console.log('4');
})
})

测试1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
console.log(1)

setTimeout(() => {
console.log(2)
new Promise(resolve => {
console.log(4)
resolve()
}).then(() => {
console.log(5)
})
})

new Promise(resolve => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})

setTimeout(() => {
console.log(9)
new Promise(resolve => {
console.log(11)
resolve()
}).then(() => {
console.log(12)
})
})

浏览器执行如下:

  1. 同步运行的代码首先输出:1、7
  2. 接着,清空microtask队列:8
  3. 第一个task执行:2、4
  4. 接着,清空microtask队列:5
  5. 第二个task执行:9、11
  6. 接着,清空microtask队列:12

node执行如下:
1 7 8 2 4 9 11 5 12

node执行下是先执行所有的setTimeout,在处理microtask队列


测试2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
console.log(1)

setTimeout(() => {
console.log(2)
new Promise(resolve => {
console.log(4)
resolve()
}).then(() => {
console.log(5)
})
process.nextTick(() => {
console.log(3)
})
})

new Promise(resolve => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})

process.nextTick(() => {
console.log(6)
})

输出1 7 6 8 2 4 3 5

说明Promise和process.nextTick虽然同时属于微任务,但是Promise的优先级较低


测试3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
console.log(1)

setTimeout(() => {
console.log(2)
new Promise(resolve => {
console.log(4)
resolve()
}).then(() => {
console.log(5)
})
process.nextTick(() => {
console.log(3)
})
})

new Promise(resolve => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})

process.nextTick(() => {
console.log(6)
})

setTimeout(() => {
console.log(9)
process.nextTick(() => {
console.log(10)
})
new Promise(resolve => {
console.log(11)
resolve()
}).then(() => {
console.log(12)
})
})

输出1 7 6 8 2 4 9 11 3 10 5 12


测试4:

1
2
3
4
5
6
7
8
9
10
11
12

setTimeout(() => {
console.log(2)
}, 2)

setTimeout(() => {
console.log(1)
}, 1)

setTimeout(() => {
console.log(0)
}, 0)

在node下输出结果有时候为 1 2 0,2 1 0,1 0 2(为什么呢)
在浏览器下出输出结果为1 0 2


Node Event Loop

  1. expired timers and interval
  2. I/O events
  3. immediates
  4. close handlers

同步任务及每个阶段都会清空microtask微任务

  1. next tick queue (process.nextTick)
  2. other queue (Promise)